/**
 * @ author HuiSir
 * @ date 2018-04-07
 */

/* 引入插件或node包 */
const path = require('path');	//node自带path组件用于处理路径，直接引用无需下载
const Webpack = require('webpack');	//用于版权信息、自带插件
const HtmlWebpackPlugin = require('html-webpack-plugin');					//自动生成html,处理单页应用或简单多页应用时较方便
const CssExtractPlugin = require('mini-css-extract-plugin');				//css文件分离
const OptimizeCssPlugin = require('optimize-css-assets-webpack-plugin');	//css压缩，配合mini-css-extract-plugin使用
const CleanWebpackPlugin = require("clean-webpack-plugin");					//清除多余打包文件
const CopyWebpackPlugin = require("copy-webpack-plugin");					//拷贝文件到打包目录


/* 输出配置文件 */	
module.exports = {
	
	/* 入口文件 */
	entry: {	//多个入口文件，同时生成多个出口文件
		main:__dirname+"/src/js/main.js", 
		other:__dirname+"/src/js/other.js",
	},
	
	/* 出口文件 */
	output: {						//出口文件信息
		filename: 'js/[name].[hash:8].js',		//打包后的目录及文件名，[name]代表入口文件名，加[hash]以区分文件，避免覆盖和缓存
		chunkFilename:'js/[name].[hash:8].js',	//非主入口文件名如require.ensure方法按需异步加载的模块	
		path: path.resolve(__dirname, 'dist'),	//打包后放置的位置，默认为dist
		//publicPath:'',	//资源文件引用目录:在上线时可以替换CDN或数据盘路径，用于存储静态文件如img/js/css
		                    //publicPath也可以单独放到某个loader的配置项中，使某一类文件加上引用目录
	},
	
	/* 配置webpack测试服务器，依赖webpack-dev-server包 */
	devServer:{	
		port:'3000',  					//若不配置默认为8080
		contentBase:__dirname+"/dev",	//测试服务器所加载页面所在的目录(在此目录下生成不可见的内存文件用于测试)
		//progress:true,					//启动时添加进度条(一般不需要)
		open:true,						//自动打开浏览器加载项目
		compress:true, 					//项目进行gzip压缩
		inline: true,					//项目修改实时刷新(重新加载)
		//hot: true, 		//热更新，在启动server时在内存中生成webpack打包后的源文件(inline自动刷新功能就行，热更新目前有bug)
		//*********************************************************************
		before(app){	//接口模拟钩子，可以模拟一些数据(这个功能挺好用)
			app.get('/user',(req,res)=>{	//当ajax使用get请求'/user'时候返回
				res.json({name:'HuiSer',age:'19'})
			})
		},
		//**********************************************************************
// 		proxy:{	//设置代理，访问'/api'可以被代理到其他域，可以跨域请求(待测试，暂时没什么用)
// 			'/api':{	//访问'/api'下的所有路径
// 				target:'http://192.168.1.2:8080',	//代理的地址
// 				pathRewrite:{'/api':''}	,//重写，路径'/api'替换为空，
// 				//比如访问http://localhost:3000/api/user会代理到http://192.168.1.2:8080/user
// 			},	
// 		}
	},
	
	/*警告处理*/
	performance: {
		hints: false   //不展示警告，避免server中的黄字
		//hints: 'error' //将展示一条错误，通知你这是体积大的资源。在开发环境中推荐~
	},
	
	/* 源码映射(用于开发测试环境，生产环境不需要。一般使用devServer测试服务器测试,这样生成的文件都在内存中) */
	devtool:'source-map',	//直接生成map映射文件用于调试，标识出错的列和行，大而全，一般使用这个足够
	//devtool:'eval-source-map',	//不会生成单独的map映射文件而是集成到js中，标识出错的列和行
	//devtool:'cheap-module-eval-source-map',	//不会生成单独的map映射文件而是集成到js中，出错的代码也不会显示列
	//devtool:'cheap-module-source-map',	//直接生成map映射文件，但不能用于调试，用于保存副本以备以后查看，出错的代码不会显示列
	
	/**
	 * 监控代码变化，一般用于实时打包 
	 * 用于需要打包上线的时候或者上线后改BUG时，因为修改完后需要直接打包上线
	 * 在开发阶段也用不到
	 */
	watch:true,	
	watchOptions:{	//监控的选项
		poll:1000,	//每秒询问1000次
		aggregateTimeout:500,	//超时，防抖，避免更改代码太快一直打包影响性能，500毫秒内输入代码只打包一次
		ignored:/node_modules/	//忽略module文件夹，避免监控node模块文件夹
	},
	
	/* 优化项 */
// 	optimization:{
// 		splitChunks: {	//分割代码块，抽离公共代码和库
// 		  // 抽离入口文件公共模块为commons模块
// 		  cacheGroups: {	//打包过程中将抽离的代码放置到缓存组内
// 			commons: {
// 			  name: "commons",	//chunk的名字，这个需要插入到html插件的chunks数组中(如若需要)，否则会出大错
// 			  chunks: "initial",	//从入口处开始抽离(initial最初的原始的)
// 			  minChunks: 2	//大于等于2次调用就要抽离
// 			},
// 			vendor:{
// 				priority:1,	//权重，先提取，避免和commons中的代码混到一块，先抽离第三方库
// 				test: /node_modules/,	//第三方模块库，诸如jquery...
// 				name: "vendor",	//chunk的名字，这个需要插入到html插件的chunks数组中
// 				chunks: "initial",
// 				minChunks: 2	//大于等于2次调用就要抽离
// 			}
// 		  }
// 		}
// 	},
	
	/* 模块和loader */
	module:{
		//优化打包速度之noParse
		noParse:/jquery/,	//不去解析引用库中的依赖关系，如果引入的库很大，会提示很多效率
		
		//配置各种模块、loader
		rules:[
			/* ESlint校验(由于在实际开发中不可能很细致的去校验，所以此功能在小项目中一般不会用到) */
			/* {
				test: /(\.jsx|\.js)$/,
				use:{
					loader:'eslint-loader',
					options:{
						enforce:'pre',		//由于这里的规则是至下而上执行的，会先执行babel-loader,所以用enforce字段注明优先执行（pre-previous）
						//enforce:'post'	//如若想要在之后执行，使用
					}
				}
			}, */
			
			/* 字体文件loader */
			{
                test: /\.(woff|woff2|svg|eot|ttf)$/i,
                use: 'url-loader?prefix=font&name=font/[name].[ext]'
            },
			
			/* 图片 Loader 放到规则的最前面（自下而上的执行顺序） */
			{
			    test: /\.(jpg|jpeg|svg|png|gif)$/i,	//i忽略大小写
				include:path.resolve(__dirname,'src/img'),	//限定只处理img目录
			    /* use:{
					loader:'url-loader',
					options:{
						limit:3*1024,			//图片小于3k时就转换为base64格式
						outputPath:'img/',		//图片默认打包到img目录下
						name:'[name].[hash:8].[ext]'	//图片生成的文件名
						publicPath:'',	//资源文件引用目录:在上线时可以替换CDN或数据盘路径，用于存储静态文件如img/js/css
						//在这里使用publicPath的话，只给图片加引用路径，其他资源不会加，这里用了，在output中不要用
					}
				} */
				use:"url-loader?limit=3072&name=img/[name].[hash:8].[ext]",	//使用query方式简写配置项
			},
			
			/* html Loader */
			{
			    test: /\.html$/,
			    use: [
					"html-withimg-loader",	//实现html中的img标签引用的图片打包
					'underscore-template-loader',	//html使用@require('url..')实现模块化调用(都需安装相应loader)
				]
			},
			
			/* JS Loader ES6解析*/
			{
				test: /(\.jsx|\.js)$/,		//语法规则，使用正则语法。(这里的jsx很少用到,react会用到jsx语法)
                use: {						//ES6解析器
					loader:'babel-loader',
					options:{
						presets:[	//预设
							'@babel/preset-env'		//进行转换
						],
						plugins: [					//处理一些提案中的高级语法
							//["@babel/plugin-proposal-decorators", { "legacy": true }],
							//["@babel/plugin-proposal-class-properties", { "loose" : true }],
							"@babel/plugin-transform-runtime",
							"@babel/plugin-syntax-dynamic-import"	//懒加载实现插件
						],
					}
				},	 
				include:path.resolve(__dirname,'src/js'),	//限定只处理js目录，因为/node_modules/目录的js都是用ES5写的
                //exclude: /node_modules/  	//排除查询模块库的js(正则)，节省打包时间()
			},
			
			/* CSS Loader */ 
			{
				test:/\.css$/,
				use: [
					{	//插件loader,将style标签样式提取到css文件中(这里也可以用style-loader将css提取到文档head标签中)
						loader: CssExtractPlugin.loader,
						options: {
							publicPath: '../'	,
							//'../'会在css中的资源文件如前添加../，因为在配置CssExtractPlugin时将文件输出到css/目录下，可避免图片路径出错
						}
					},
	                { 
	                	loader: 'css-loader', 	//使你能够使用类似@import的方法实现样式表require的功能
	                },
	                {
	                	loader:"postcss-loader",	//配合autoprefixer插件使用
	                	options: {	//单独配置某个loader,这里也可以在根目录中添加postcss.config.js中配置
	                        plugins: [
						        require('autoprefixer')	//可以为CSS代码自动添加适应不同浏览器的CSS前缀插件
	                        ]
                    	}
	                }
                ]
			},
			
			/* SASS Loader */
			{
                test: /(\.scss|\.sass)$/,
				include:path.resolve(__dirname,'src/css'),	//sass文件一般是自己写的，只解析css目录就ok了，但css文件可能有外部的
                use: [
	                {	//插件loader,将style标签样式提取到css文件中(这里也可以用style-loader将css提取到文档head标签中)
	                	loader: CssExtractPlugin.loader,
	                	options: {
							publicPath: '../'
	                	}
	                },
	                'css-loader',
	                {
	                	loader:"postcss-loader",
	                	options: {	
	                        plugins: [
						        require('autoprefixer')
	                        ]
                    	}
	                },
	                "sass-loader"
                ]
            },
			
			/* EJS Loader */
		]
	},
	
	/**
	 * 插件，一般是class类形式，使用new创建 
	 * 插件，用于生产模板和各项功能，放置所有webpack插件
	 */ 
	plugins:[
		/**
		 * html插件配置(如若为多页面项目，需要new多个新的HtmlWebpackPlugin对象，以便配置不同的模板和资源包)
		 * HtmlWebpackPlugin支持EJS语法
		 */
		
		/* index页 */
        new HtmlWebpackPlugin({						
        	template: __dirname + "/src/index.html",	//自动生成依据的模板
        	filename: "index.html", 						//打包后的文件名
        	favicon: __dirname + "/src/favicon.ico",		//favicon图标
        	minify: {									//压缩html
            	removeComments: true, 					//移除注释
            	collapseInlineTagWhitespace: true, 		//移除行间空格
            	collapseWhitespace:true,				//移除空行
				removeAttributeQuotes:true,				//删除属性双引号(极端操作,如若出错可关闭)
       		},
        	chunks:['main','commons','vender'], 							//需要引用的资源包,在entry入口文件和splitChunks公共库中定义过的
       		/*传参（使用ejs调用<%= htmlWebpackPlugin.options.data.title %>）*/
       		data:{
				title:'智能眼科学会官网'
			},
			//hash:true,		//给相关资源(如js/css等)添加hash戳避免缓存(会将hash添加到query：main.js?0122asd13asd3d3as)
								//但既然在打包的文件名中添加了8位hash，那么这里可以不用添加，要不然就会增加代码重量
        	//excludeChunks:['btn','b'],				//如果资源包过多，可以写需要排除的资源包
        	//inject:"head" 							//指定将js插入到head标签中，也可以是body，默认body
        }),
		
		/* other页 */
		new HtmlWebpackPlugin({						
			template: __dirname + "/src/other.html",
			filename: "other.html", 				
			favicon: __dirname + "/src/favicon.ico",		
			minify: {									
				removeComments: true, 					
				collapseInlineTagWhitespace: true, 	
				collapseWhitespace:true,			
				removeAttributeQuotes:true,	
			},
			chunks:['other','commons','vender'], 
		}),
		
		/*css文件分离插件配置*/
        new CssExtractPlugin({
	      	filename: "css/[name].[hash:8].css",		//抽离出的主要css样式文件名
	      	chunkFilename: "css/[id].css"         	//非主要css样式文件名
    	}),
		
		/* css压缩优化(一般用于生产环境，开发环境一般不用压缩，以便找到样式位置随机修改样式) */
		new OptimizeCssPlugin(),
		
		/*清除多余打包文件，用于生产环境上线的时候，可能会开启自动打包，频繁打包由于hash的缘故会生成很多文件*/
		new CleanWebpackPlugin(),
		
		/* 定义环境变量，向js传参(偶尔使用，因为这样传参会直接污染全局变量) */
		new Webpack.DefinePlugin({
			//__mode:'DEV',	//注意这样写的话，传的是DEV变量而不是字符串，会找不到，需要转换位字符串
			__mode:JSON.stringify('DEV'),	//注意这样写的话，传的是DEV变量而不是字符串，会找不到，需要转换位字符串(使用双下划线避免污染全局变量)
			__name:JSON.stringify('..........HuiSir.........'),
			__num:'1+1',	//这样传的是number类型，值为2，不是字符串'1+1'
			__str:"'1+1'",	//这样传的是字符串类型，值为'1+1'
		}),
		
		/* 可以避免打包模块中多余的资源，避免打包的代码过大 */
		//new Webpack.IgnorePlugin(/****/,/****/) //第一个参数是需要忽略的资源目录，第二个参数是模块名字，两个参数都应该是正则
		
		/* 添加版权信息 */
		new Webpack.BannerPlugin('@author HuiSir版权所有;@introduce xxx项目'),	//版权信息插入到js/css文件头部
		
		/* 拷贝某些文件到打包目录,多个文件夹可添加多项数组，这个功能暂时用不到 */
// 		new CopyWebpackPlugin([
// 			{from:'doc',to:'./doc'}	//将doc目录文件拷到dist/doc下
// 		])
	],		
};